package series

import (
	"fmt"
	"math"
	"strconv"
	"strings"
)

type stringElement struct {
	e string
}

// force stringElement struct to implement Element interface
var _ Element = (*stringElement)(nil)

func (e *stringElement) Set(value interface{}) error {
	switch val := value.(type) {
	case string:
		e.e = string(val)
	case int:
		e.e = strconv.Itoa(val)
	case int8:
		e.e = strconv.Itoa(int(val))
	case int16:
		e.e = strconv.Itoa(int(val))
	case int32:
		e.e = strconv.Itoa(int(val))
	case int64:
		e.e = strconv.FormatInt(val, 10)
	case uint:
		e.e = strconv.FormatUint(uint64(val), 10)
	case uint8:
		e.e = strconv.FormatUint(uint64(val), 10)
	case uint16:
		e.e = strconv.FormatUint(uint64(val), 10)
	case uint32:
		e.e = strconv.FormatUint(uint64(val), 10)
	case uint64:
		e.e = strconv.FormatUint(val, 10)
	case float32:
		e.e = strconv.FormatFloat(float64(val), 'f', 6, 32)
	case float64:
		e.e = strconv.FormatFloat(val, 'f', 6, 64)
	case bool:
		b := value.(bool)
		if b {
			e.e = "true"
		} else {
			e.e = "false"
		}
	case Element:
		e.e = val.String()
	default:
	}
	return nil
}

func (e stringElement) Copy() Element {
	return &stringElement{e.e}
}

func (e stringElement) IsNA() bool {
	return false
}

func (e stringElement) Type() Type {
	return String
}

func (e stringElement) Val() ElementValue {
	return string(e.e)
}

func (e stringElement) String() string {
	return string(e.e)
}

func (e stringElement) Int() (int, error) {
	return strconv.Atoi(e.e)
}

func (e stringElement) Int8() (int8, error) {
	v, err := strconv.Atoi(e.e)
	return int8(v), err
}

func (e stringElement) Int16() (int16, error) {
	v, err := strconv.Atoi(e.e)
	return int16(v), err
}

func (e stringElement) Int32() (int32, error) {
	v, err := strconv.ParseInt(e.e, 10, 32)
	return int32(v), err
}

func (e stringElement) Int64() (int64, error) {
	v, err := strconv.ParseInt(e.e, 10, 64)
	return v, err
}

func (e stringElement) Float32() float32 {
	f, err := strconv.ParseFloat(e.e, 32)
	if err != nil {
		return float32(math.NaN())
	}
	return float32(f)
}

func (e stringElement) Float64() float64 {
	f, err := strconv.ParseFloat(e.e, 64)
	if err != nil {
		return math.NaN()
	}
	return f
}

func (e stringElement) Bool() (bool, error) {
	switch strings.ToLower(e.e) {
	case "true", "t", "1":
		return true, nil
	case "false", "f", "0":
		return false, nil
	}
	return false, fmt.Errorf("can't convert String \"%v\" to bool", e.e)
}

func (e stringElement) Eq(elem Element) bool {
	if e.IsNA() || elem.IsNA() {
		return false
	}
	return e.e == elem.String()
}

func (e stringElement) Neq(elem Element) bool {
	if e.IsNA() || elem.IsNA() {
		return false
	}
	return e.e != elem.String()
}

func (e stringElement) Less(elem Element) bool {
	if e.IsNA() || elem.IsNA() {
		return false
	}
	return e.e < elem.String()
}

func (e stringElement) LessEq(elem Element) bool {
	if e.IsNA() || elem.IsNA() {
		return false
	}
	return e.e <= elem.String()
}

func (e stringElement) Greater(elem Element) bool {
	if e.IsNA() || elem.IsNA() {
		return false
	}
	return e.e > elem.String()
}

func (e stringElement) GreaterEq(elem Element) bool {
	if e.IsNA() || elem.IsNA() {
		return false
	}
	return e.e >= elem.String()
}
